package com.melichercik.code;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

import android.app.Activity;
import android.graphics.Canvas;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;

public class Code implements Serializable {
	
	public ArrayList<Brick> code = new ArrayList<Brick>();
	
	public Code() {
		code.add(new SimpleBrick(Brick.START));
		code.add(new SimpleBrick(Brick.END));
		setRenderPositions();
	}
	
	public synchronized void addBrick(Brick brick) {
		code.add(brick);
		
		if (brick.getType() == Brick.LOOP) {
			addLoopEnd(brick, -1);
		} else if(brick.getType() == Brick.WHILE) {
			addLoopEnd(brick, -1);
		} else if(brick.getType() == Brick.IF) {
			addElse(brick, -1);
		}
		
		setRenderPositions();
	}
	
	public synchronized Brick addBrick(Brick brick, int index) {
		code.add(index, brick);
		
		if (brick.getType() == Brick.LOOP) {
			addLoopEnd(brick, index);
		} else if(brick.getType() == Brick.WHILE) {
			addLoopEnd(brick, index);
		} else if(brick.getType() == Brick.IF) {
			addElse(brick, index);
		}
		
		setRenderPositions();
		
		return brick;
	}
	
	public synchronized void removeBrick(Brick brick) {
		
		if (brick.getType() == Brick.LOOP || brick.getType() == Brick.WHILE) {
			LoopBrick tmpBrick = (LoopBrick) brick;
			code.remove(tmpBrick.end);
		} else if (brick.getType() == Brick.IF) {
			IfBrick tmpBrick = (IfBrick) brick;
			code.remove(tmpBrick.then);
			code.remove(tmpBrick.endthen);
			code.remove(tmpBrick.els);
			code.remove(tmpBrick.endels);
			code.remove(tmpBrick.endif);
		}
		
		code.remove(brick);		
		
		setRenderPositions();
	}
	
	public String compileCode() {
		CodeCompiler compiler = new CodeCompiler(code);
		
		Log.d("MyDebug", compiler.getCode());
		
		return compiler.getCode();
	}
	
	public synchronized void renderCode(Canvas canvas, int offsetX, int offsetY, int scale) {
		CodePainter painter = new CodePainter(canvas, code, offsetX, offsetY, scale);
		
		painter.renderCode();
	}
	
	//debug sekvencie kodu
	public void debugBrickSequence() {
		
		String output = "";
		
		for (Brick brick : code) {
			output += "-> " + brick.getType() + " ";
		}
		
		Log.d("MyDebug", output);
	}
	
	public synchronized Brick click(int x, int y) {
		
		for (Brick brick : code) {
			if (brick.click(x, y)) {
				return brick;
			}
		}
		
		return null;
	}
	
	public int indexOfBrick(Brick brick) {
		
		return code.indexOf(brick);
	}
	
	public int getBrickLoopDepth(Brick brick) {
		int depth = 0;
		
		for (Brick tmpBrick: code) {
			if (tmpBrick.getType() == Brick.LOOP) {
				depth++;
			} else if (tmpBrick.getType() == Brick.ENDLOOP) {
				depth--;
			}
			
			if (tmpBrick == brick) {
				return depth;
			}
		}
		
		return depth;
	}
	
	private void addLoopEnd(Brick brick, int index) {
		String type = "";
		if (brick.getType() == Brick.LOOP) {
			type = Brick.ENDLOOP;
		} else if  (brick.getType() == Brick.WHILE) {
			type = Brick.ENDWHILE;
		}
		
		SimpleBrick endLoop = new SimpleBrick(type);
		((LoopBrick) brick).end = endLoop;

		if (index > -1) {
			code.add(++index, endLoop);
		} else {
			code.add(endLoop);
		}
	}
	
	private void addElse(Brick brick, int index) {
		HelperBrick then = new HelperBrick(Brick.THEN, brick);
		((IfBrick) brick).then = then;
		
		HelperBrick endthen = new HelperBrick(Brick.ENDTHEN, brick);
		((IfBrick) brick).endthen = endthen;
		
		HelperBrick els = new HelperBrick(Brick.ELSE, brick);
		((IfBrick) brick).els = els;
		
		HelperBrick endels = new HelperBrick(Brick.ENDELSE, brick);
		((IfBrick) brick).endels = endels;
		
		HelperBrick endif = new HelperBrick(Brick.ENDIF, brick);
		((IfBrick) brick).endif = endif;
			
		if (index > -1) {
			code.add(++index, then);
			code.add(++index, endthen);
			code.add(++index, els);
			code.add(++index, endels);
			code.add(++index, endif);
		} else {
			code.add(then);
			code.add(endthen);
			code.add(els);
			code.add(endels);
			code.add(endif);
		}
	}
	
	private void setRenderPositions() {
		int x = 0;
		int y = 0;
		
		for(Brick brick: code) {
			brick.x = x;
			brick.y = y;
			x = setRenderX(brick, x);
			y = setRenderY(brick, y);
		}
		
		stretchBranchesWidth();
//		distendBranches();
	}
	
	private int setRenderX(Brick brick, int x) {
		String type = brick.getType();
		
		if (type == Brick.IF) {
			x--; 
		} else if (type == Brick.ENDTHEN) {
			x += 2;
		} else if (type == Brick.ENDELSE) {
			x--;
		}
		
		return x;
	}
	
	private int setRenderY(Brick brick, int y) {
		String type = brick.getType();
		
		if (type == Brick.IF) {
			y = y;
		} else if (type == Brick.ENDTHEN) {
			y = ((HelperBrick) brick).parent.y;
		} else if (type == Brick.ENDELSE) {
			int endthenY = ((IfBrick) ((HelperBrick) brick).parent).endthen.y;
			if (endthenY > y)  {
				y = endthenY;
			} 
		} else {
			y++;
		}
		
		return y;
	}
	
	private void stretchBranchesWidth() {
		
		for(Brick brick: code) {
			if (brick.getType() == Brick.IF) {
				IfBrick tmpBrick = ((IfBrick) brick);
				tmpBrick.leftWidth = getBranchWidth((HelperBrick) tmpBrick.then, (HelperBrick) tmpBrick.endthen);
				tmpBrick.rightWidth = getBranchWidth((HelperBrick) tmpBrick.els, (HelperBrick) tmpBrick.endels);
//				tmpBrick.condition = tmpBrick.leftWidth + " - " + tmpBrick.rightWidth;
			}
		}
	}
	
	private int getBranchWidth(HelperBrick start, HelperBrick end) {
		HashSet<String> visited = new HashSet<String>();
		int result = 0;
		int depth = 0;
		String sides = "l";
		if (start.getType() == Brick.ELSE) {
			sides = "r";
		}
		boolean counting = false;
		
		for(Brick brick: code) {
			String where = sides.substring(sides.length()-1) + Integer.toString(depth);
			
			if (brick == end) {
//				if (result > 1) {
//					result--;
//				}
				return result;
			} else if (brick == start) {
				counting = true;
			} else if (counting 
				&& brick.getType() == Brick.IF 
				&& !visited.contains(where)
			) {
				result += 1;
				visited.add(where);
				depth++;
			} else if (counting && brick.getType() == Brick.ENDIF) {
				depth--;
			} else if (counting && brick.getType() == Brick.THEN) {
				sides += "l";
			} else if (counting && brick.getType() == Brick.ENDTHEN) {
				if (sides.length() > 1) {
					sides = sides.substring(0, sides.length()-1);
				} 
			} else if (counting && brick.getType() == Brick.ELSE) {
				sides += "r";
			} else if (counting && brick.getType() == Brick.ENDELSE) {
				if (sides.length() > 1) {
					sides = sides.substring(0, sides.length()-1);
				} 
			}
			
		}
		
		return result;
	}
	
	private void distendBranches() {
		int distendX = 0;
		
		for(Brick brick: code) {
			if (brick.getType() == Brick.THEN) {
				distendX -= ((IfBrick) ((HelperBrick) brick).parent).leftWidth;
				
			} else if (brick.getType() == Brick.ELSE) {
				distendX += ((IfBrick) ((HelperBrick) brick).parent).rightWidth;
				
			}
			brick.x += distendX;
			
			if (brick.getType() == Brick.ENDELSE) {
				distendX -= ((IfBrick) ((HelperBrick) brick).parent).rightWidth;
				
			} else if (brick.getType() == Brick.ENDTHEN) {
				distendX += ((IfBrick) ((HelperBrick) brick).parent).leftWidth;
				
			} 
			
		}
	}
	
	public void restoreCode(ArrayList<Brick> savedCode) {
		
		int index = 0;
		
		for(Brick brick: savedCode) {
			if (brick.getType().contains(Brick.DELAY)) {
				SimpleBrick tmpBrick = new SimpleBrick(Brick.DELAY);
				tmpBrick.var1 = ((SimpleBrick) brick).var1;
				addBrick((tmpBrick), index);
				
			} else if (brick.getType().contains(Brick.ROLL)) {
				SimpleBrick tmpBrick = new SimpleBrick(Brick.ROLL);
				tmpBrick.var1 = ((SimpleBrick) brick).var1;
				tmpBrick.var2 = ((SimpleBrick) brick).var2;
				addBrick(tmpBrick, index);
				
			} else if (brick.getType().contains(Brick.STOP)) {
				SimpleBrick tmpBrick = new SimpleBrick(Brick.STOP);
				addBrick(tmpBrick, index);
				
			} else if (brick.getType().contains(Brick.COLOR)) {
				SimpleBrick tmpBrick = new SimpleBrick(Brick.COLOR);
				tmpBrick.var1 = ((SimpleBrick) brick).var1;
				tmpBrick.var2 = ((SimpleBrick) brick).var2;
				tmpBrick.var3 = ((SimpleBrick) brick).var3;
				addBrick(tmpBrick, index);
				
			} else if (brick.getType().contains(Brick.LED)) {
				SimpleBrick tmpBrick = new SimpleBrick(Brick.LED);
				tmpBrick.var1 = ((SimpleBrick) brick).var1;
				addBrick(tmpBrick, index);
				
			} else if (brick.getType().contains(Brick.PRINT)) {
				SimpleBrick tmpBrick = new SimpleBrick(Brick.PRINT);
				tmpBrick.var1 = ((SimpleBrick) brick).var1;
				addBrick(tmpBrick, index);
				
			} else if (brick.getType().contains(Brick.SOUND)) {
				SimpleBrick tmpBrick = new SimpleBrick(Brick.SOUND);
				addBrick(tmpBrick, index);
				
			} else if (brick.getType().contains(Brick.VARIABLE)) {
				SimpleBrick tmpBrick = new SimpleBrick(Brick.VARIABLE);
				tmpBrick.var1 = ((SimpleBrick) brick).var1;
				tmpBrick.var2 = ((SimpleBrick) brick).var2;
				addBrick(tmpBrick, index);
				
			} else if (brick.getType().contains(Brick.LOOP) && !brick.getType().contains(Brick.ENDLOOP)) {
				LoopBrick tmpBrick = new LoopBrick(Brick.LOOP);
				tmpBrick.var1 = ((LoopBrick) brick).var1;
				tmpBrick.var2 = ((LoopBrick) brick).var2;
				tmpBrick.var3 = ((LoopBrick) brick).var3;
				addBrick(tmpBrick, index);
				
			} else if (brick.getType().contains(Brick.WHILE) && !brick.getType().contains(Brick.ENDWHILE)) {
				LoopBrick tmpBrick = new LoopBrick(Brick.WHILE);
				tmpBrick.var1 = ((LoopBrick) brick).var1;
				addBrick(tmpBrick, index);
					
			} else if (brick.getType().contains(Brick.IF) && !brick.getType().contains(Brick.ENDIF)) {
				IfBrick tmpBrick = new IfBrick();
				tmpBrick.condition = ((IfBrick) brick).condition;
				addBrick(tmpBrick, index);
			}
			
			index++;
		}
	}
}
